package com.twu.biblioteca;

import org.jasypt.util.password.ConfigurablePasswordEncryptor;

import java.util.List;
import java.util.Map;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newLinkedHashMap;

/**
 * biblioteca
 * <p/>
 * User: Ziyi.LIU
 * Date: 14-6-17
 * Time: 下午3:46
 */

public enum Library {
    INSTANCE;

    private Map<Material, User> libraryMap = newLinkedHashMap();
    private List<User> libraryUserList = newArrayList();

    private Library() {
        libraryMap.put(new Book(1, "Nineteen Eighty-Four", "George Orwell", 1949), null);
        libraryMap.put(new Book(2, "The C Programming Language", "Brian Kernighan & Dennis Ritchie", 1978), null);
        libraryMap.put(new Book(3, "Thinking in C++, Vol 1", "Bruce Eckel", 2000), null);

        libraryMap.put(new Movie(4, "Casablanca", "Michael Curtiz", 1942, 10), null);
        libraryMap.put(new Movie(5, "Curse of the Golden Flower", "Zhang Yimou", 2006, 6), null);
        libraryMap.put(new Movie(6, "The Shawshank Redemption", "Frank Darabont", 1994, 9), null);
        libraryMap.put(new Movie(7, "The Hobbit: The Desolation of Smaug", "Peter Jackson", 2013, 0), null);
        libraryUserList.add(new User("JohnDoe",
                "fEqNCco3Yq9h5ZUglD3CZJT4lBs=",
                "librarian@example.com",
                "1-800-432-3838"));
        libraryUserList.add(new User("FooBar",
                "fEqNCco3Yq9h5ZUglD3CZJT4lBs=",
                "foobar@example.com",
                "8008208820"));
        libraryUserList.add(new User("admin",
                "0DPiKuNIrrVmD8IUCuw1hQxNqZc=",
                "admin@example.com",
                "+1 911"));
        libraryUserList.add(new User("twu",
                "1ZXEhuYyCw8eXEURYiJ2ldHjqHM=",
                "testuser@example.com",
                "+1 234 567 8910"));
    }


    public String listAllAvailableBooks() {
        String s = "";
        for(Material m: libraryMap.keySet()) {
            if(m != null && m.getClass() == Book.class) {
                if(libraryMap.get(m) == null) {
                    s += m.toString();
                    s += "\n";
                }
            }
        }
        return s;
    }

    public String listAllAvailableMovies() {
        String s = "";
        for(Material m: libraryMap.keySet()) {
            if(m != null && m.getClass() == Movie.class) {
                if(libraryMap.get(m) == null) {
                    s += m.toString();
                    s += "\n";
                }
            }
        }
        return s;
    }

    public Material checkoutMaterial(int materialId, User user) throws MaterialCheckOutException {
        for(Material m: libraryMap.keySet()) {
            if(m.getId() == materialId) {
                if(libraryMap.get(m) != null) {
                    throw new MaterialCheckOutException("material already borrowed");
                } else {
                    libraryMap.put(m, user);
                    return m;
                }
            }
        }

        throw new MaterialCheckOutException("material not found in library");
    }

    public boolean isAvailable(int materialId) {
        for(Material m: libraryMap.keySet()) {
            if(m.getId() == materialId) {
                //material found in library, return its availability status
                return libraryMap.get(m) == null;
            }
        }

        //book not found in library
        return false;
    }

    public boolean returnMaterial(int materialId) throws MaterialReturnException {
        for(Material m: libraryMap.keySet()) {
            if(m.getId() == materialId) {
                if(libraryMap.get(m) == null) {
                    throw new MaterialReturnException("material already returned");
                } else {
                    libraryMap.put(m, null);
                    return true;
                }
            }
        }

        throw new MaterialReturnException("material is not from this library");
    }

    public boolean returnMaterial(Material material, User user) throws MaterialReturnException {
        for(Material m: libraryMap.keySet()) {
            if(m.equals(material)) {
                if(libraryMap.get(m) == null) {
                    throw new MaterialReturnException("material already returned");
                } else if(libraryMap.get(m) != user) {
                    throw new MaterialReturnException("material is not lent to this user");
                } else {
                    libraryMap.put(m, null);
                    return true;
                }
            }
        }
        throw new MaterialReturnException("material is not from this library");
    }

    public boolean returnMaterial(int materialId, User user) throws MaterialReturnException {
        for(Material m: libraryMap.keySet()) {
            if(m.getId() == materialId) {
                if(libraryMap.get(m) == null) {
                    throw new MaterialReturnException("material already returned");
                } else if(libraryMap.get(m) != user) {
                    throw new MaterialReturnException("material is not lent to this user");
                } else {
                    libraryMap.put(m, null);
                    return true;
                }
            }
        }
        throw new MaterialReturnException("material is not from this library");

    }

    public User authenticate(String username, String password) {

        ConfigurablePasswordEncryptor passwordEncryptor = new ConfigurablePasswordEncryptor();
        passwordEncryptor.setAlgorithm("SHA-1");
        passwordEncryptor.setPlainDigest(true);

        for(User user: libraryUserList) {
            if(user.getUsername().equals(username)) {
                if(passwordEncryptor.checkPassword(password, user.getEncryptedPassword()))
                    return user;
                else
                    return null;
            }
        }
        return null;
    }
}

